package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.example.Exception.BusinessException;
import org.example.Exception.ForbiddenException;
import org.example.Exception.RollBackException;
import org.example.domain.response.ResponseResult;
import org.example.domain.response.ResponseResultEnum;
import org.example.utils.ResponseUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;

/**
 * 统一拦截异常
 */
@RestControllerAdvice(basePackages = "org.example.controller")
public class ExceptionAdvice {

    /**
     * 捕获 {@code BusinessException} 异常
     */
    @ExceptionHandler({BusinessException.class})
    public ResponseResult<?> handleBusinessException(BusinessException ex) {
        return ResponseResult.failed(ex.getMessage());
    }

    /**
     * 捕获 {@code ForbiddenException} 异常
     */
    @ExceptionHandler({ForbiddenException.class})
    public ResponseResult<?> handleForbiddenException(ForbiddenException ex) {
        return ResponseResult.failed(ex.getMessage());
    }

    /**
     * {@code @RequestBody} 参数校验不通过时抛出的异常处理
     * 参数是对象时，校验不通过时报错
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResponseResult handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败:");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        String msg = sb.toString();
        if (StringUtils.hasText(msg)) {
            return ResponseResult.failed(ResponseResultEnum.VALIDATE_FAILED.getCode(), msg);
        }
        return ResponseResult.failed(ResponseResultEnum.VALIDATE_FAILED);
    }

    /**
     * {@code @PathVariable} 和 {@code @RequestParam} 参数校验不通过时抛出的异常处理
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseResult handleConstraintViolationException(ConstraintViolationException ex) {
        if (StringUtils.hasText(ex.getMessage())) {
            return ResponseResult.failed(ResponseResultEnum.VALIDATE_FAILED.getCode(), ex.getMessage());
        }
        return ResponseResult.failed(ResponseResultEnum.VALIDATE_FAILED);
    }

    /**
     * SpringSecurity 授权过程出现异常的返回
     * 由于授权异常会先被全局异常捕获，要在自定义的异常中处理需要抛出
     * 也可以直接在这里处理
     */
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseResult accessDeniedException(AccessDeniedException e){
        e.printStackTrace();
        return ResponseResult.failed(ResponseResultEnum.FORBIDDEN);
    }

    /**
     * SpringSecurity 认证过程出现异常的返回
     * 由于授权异常会先被全局异常捕获，要在自定义的异常中处理需要抛出
     * 也可以直接在这里处理
     */
    @ExceptionHandler(AuthenticationException.class)
    public ResponseResult authenticationException(AuthenticationException e){
        e.printStackTrace();
        return ResponseResult.failed("402",ResponseResultEnum.AUTH_ERROR.getMsg());
    }

    /**
     * 捕获数据库事务失败异常
     */
    @ExceptionHandler({RollBackException.class})
    public ResponseResult DBException(RollBackException ex) {
//        ex.printStackTrace();
        return ResponseResult.failed(ResponseResultEnum.INTER_SERVER_ERROR.getCode(), ex.getMessage());
    }
    /**
     * 顶级异常捕获并统一处理，当其他异常无法处理时候选择使用
     */
    @ExceptionHandler({Exception.class})
    public ResponseResult<?> handle(Exception ex) {
        ex.printStackTrace();
        return ResponseResult.failed(ResponseResultEnum.INTER_SERVER_ERROR);
    }
}
